#include "interrupt.h"
#include "stdint.h"
#include "global.h"
#include "io.h"
#include "print.h"

#define IDT_DESC_CNT 0x81   // 总共支持中断数

#define PIC_M_CTRL 0x20    // 主片的控制端口
#define PIC_M_DATA 0x21    // 主片的数据端口
#define PIC_S_CTRL 0xa0    // 从片的控制端口
#define PIC_S_DATA 0xa1    // 从片的数据端口

#define EFLAGS_IF 0x00000200    // eflags if 位置1, 接受可屏蔽中断
#define GET_EFLAGS(EFLAGS_VAR) asm volatile("pushfl; popl %0" : "=g" (EFLAGS_VAR))                                

struct gate_desc {
    uint16_t func_offset_low_word;    // 中断处理程序入口偏移值的低16bit
    uint16_t selector;                // 中断处理程序的段选择子
    uint8_t dcount;                   // 固定值0
    uint8_t attribute;                // 1bit:P, 2bit:DPL, 4bit:Type, 1bit:固定
    uint16_t func_offset_high_word;    // 中断处理程序入口偏移值的高16bit
};

/* 创建中断门描述符*/
static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler func); 
extern uint32_t syscall_handler();
/* 中断门描述符表*/
static struct gate_desc idt[IDT_DESC_CNT];
extern intr_handler intr_entry_table[IDT_DESC_CNT];    // 定义在kernel.S, 最终调用ide_table中C的中断处理程序 
char* intr_name[IDT_DESC_CNT];     // 保存异常名字
intr_handler idt_table[IDT_DESC_CNT];    // C的中断处理程序入口

/* 初始化可编程中断控制器8259A*/
static void pic_init() {
    // 主片
    outb (PIC_M_CTRL, 0X11);    // ICW1: 00010001, 边沿触发, 级联, 需要ICW4
    outb (PIC_M_DATA, 0x20);    // 起始中断向量号0x20
    outb (PIC_M_DATA, 0x04);    // 
    outb (PIC_M_DATA, 0x01);    // 

    // 从片
    outb (PIC_S_CTRL, 0X11);    // ICW1: 00010001, 边沿触发, 级联, 需要ICW4
    outb (PIC_S_DATA, 0x28);    // 起始中断向量号0x28
    outb (PIC_S_DATA, 0x02);    // 
    outb (PIC_S_DATA, 0x01);    // 

    // 0xf8 = 1111_1000, 左起第一个0打开IRQ0时钟中断, 
    // 第二个0打开IRQ1键盘中断
    // 第三个0打开IRQ2从片中
    outb(PIC_M_DATA, 0xf8);
    // 0xbf = 1011_1111, 打开从片上IRQ14, 接收硬盘中断
    outb(PIC_S_DATA, 0xbf);

    put_str("pic_int done\n");

}

/* 创建中断门描述符, 并赋值对应相应的中断处理程序*/
static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler func)
{
    p_gdesc->func_offset_low_word = (uint32_t)func & 0x0000ffff;
    p_gdesc->selector = SELECTOR_K_CODE;
    p_gdesc->dcount = 0;
    p_gdesc->attribute = attr;
    p_gdesc->func_offset_high_word = ((uint32_t)func & 0xffff0000) >> 16;
} 

/* 初始化中断描述符表 */
static void idt_desc_init() {
    int i, lastindex = IDT_DESC_CNT - 1;
    // 0x00 - 0x79 特权级 0
    for(i=0; i<IDT_DESC_CNT; ++i) {
        make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]);
    }

    // 0x80 特权级 3, 系统调用
    make_idt_desc(&idt[lastindex], IDT_DESC_ATTR_DPL3, syscall_handler);
    put_str("idt, _desc_init done\n");
}

// 通用C的中断处理程序
static void general_intr_handler(uint8_t vec_nr) {
    if (vec_nr == 0x27|| vec_nr==0x2f) {
        return;
    }
    set_cursor(0);
    int cursor_pos = 0;
    while(cursor_pos < 320) {
        put_char(' ');
        cursor_pos++;
    }

    set_cursor(0);
    put_str("!!!!!!!      excepion message begin   !!!!!!!!!\n");
    set_cursor(88);
    put_str(intr_name[vec_nr]);
    if(vec_nr == 14) {
        int page_fault_vaddr = 0;
        asm ("movl %%cr2, %0" : "=r" (page_fault_vaddr));
        put_str("\npage fault addr is");
        put_int(page_fault_vaddr);
    }
    put_str("\n!!!!!!    exception message end    !!!!!!\n");
    while(1);
}

static void exception_init() {
    int i;
    for(i=0; i< IDT_DESC_CNT; ++i) {
        idt_table[i] = general_intr_handler;
        intr_name[i] = "unknown "; 
    }
    intr_name[0] = "#DE Divide Error";
    intr_name[1] = "#DB Debug Exception";
    intr_name[2] = "NMI Interrupt";
    intr_name[3] = "#BP Breakpoint Exception";
    intr_name[4] = "#OF Overflow Exception";
    intr_name[5] = "#BR BOUND Range Exceeded Exception";
    intr_name[6] = "#UD Invalid Opcode Exception";
    intr_name[7] = "#NM Device Not Available Exception";
    intr_name[8] = "#DF Double Fault Exception";
    intr_name[9] = "Coprocessor Segment Overrun";
    intr_name[10] = "#TS Invalid TSS Exception";
    intr_name[11] = "#NP Segment Not Present";
    intr_name[12] = "#SS Stack Fault Exception";
    intr_name[13] = "#GP General Protection Exception";
    intr_name[14] = "#PF Page-Fault Exception";
    // intr_name[15] 第15项是intel保留项，未使用
    intr_name[16] = "#MF x87 FPU Floating-Point Error";
    intr_name[17] = "#AC Alignment Check Exception";
    intr_name[18] = "#MC Machine-Check Exception";
    intr_name[19] = "#XF SIMD Floating-Point Exception";

}
/* 完成有关中断的所有初始化工作*/
void idt_init() {
    put_str("idt_int start\n");
    idt_desc_init();        // 初始化中断描述符表
    exception_init();
    pic_init();             // 初始化8259A

    uint64_t idt_operand = ( (sizeof(idt)-1) | ( (uint64_t) (uint32_t)idt<<16 ) );
    /* 加载idt中断描述表*/
//TODO: this have a big bug
    //uint64_t idt_operand_false = ( (sizeof(idt)-1) | ( (uint64_t) ( (uint32_t)idt<<16 ) ) );
    // 运算符顺序, ()小括号 > (type)类型转换 > <<位移 
    // 错误原因: 小括号优先, 先算( (uint32_t)idt<<16 ), 致idt高16位丢
    // 正确: 先(uint64_t)

    asm volatile("lidt %0" : : "m"(idt_operand));
    put_str("idt_init done\n");
}

enum intr_status intr_enable() {
    enum intr_status old_status;
    if(INTR_ON == intr_get_status()) {
        old_status = INTR_ON;
        return old_status;
    } else {
        old_status = INTR_OFF;
        asm volatile("sti");   // 开放INTR引脚, 接受可屏蔽中断. sti指令将IF位置1
        return old_status;                       
    }
}

enum intr_status intr_disable() {
    enum intr_status old_status;
    if(INTR_ON == intr_get_status()) {
        old_status = INTR_ON;
        asm volatile("cli" ::: "memory"); // 关INTR引脚, 屏蔽可屏蔽中断. cti指令将IF位置1
        return old_status;
    } else {
        old_status = INTR_OFF;
        return old_status;
    }
}

enum intr_status intr_set_status(enum intr_status status) {
    return status & INTR_ON ? intr_enable() : intr_disable();
}

enum intr_status intr_get_status() {
    uint32_t eflags = 0;
    GET_EFLAGS(eflags);
    return (EFLAGS_IF&eflags) ? INTR_ON : INTR_OFF;
}

void register_handler(uint8_t vector_no, intr_handler func)
{
    idt_table[vector_no] = func;
}
